home *** CD-ROM | disk | FTP | other *** search
- # A class to help applications that do fancy text formatting.
- # You create an instance each time you must redraw the window.
- # Set the initial left, top and right coordinates;
- # then feed it words, font changes and vertical movements.
- #
- # This class should eventually be extended to support much fancier
- # formatting, along the lines of TeX; for now, a very simple model
- # is sufficient.
- #
- class formatter:
- #
- # Initialize a formatter instance.
- # Pass the window's drawing object, and left, top, right
- # coordinates of the drawing space as arguments.
- #
- def __init__(self, d, left, top, right):
- self.d = d # Drawing object
- self.left = left # Left margin
- self.right = right # Right margin
- self.v = top # Top of current line
- self.center = 0
- self.justify = 1
- self.setfont('') # Default font
- self._reset() # Prepare for new line
- #
- # Reset for start of fresh line.
- #
- def _reset(self):
- self.boxes = [] # Boxes and glue still to be output
- self.sum_width = 0 # Total width of boxes
- self.sum_space = 0 # Total space between boxes
- self.sum_stretch = 0 # Total stretch for space between boxes
- self.max_ascent = 0 # Max ascent of current line
- self.max_descent = 0 # Max descent of current line
- self.avail_width = self.right - self.left
- self.hang_indent = 0
- #
- # Set the current font, and compute some values from it.
- #
- def setfont(self, font):
- self.font = font
- self.d.setfont(font)
- self.font_space = self.d.textwidth(' ')
- self.font_ascent = self.d.baseline()
- self.font_descent = self.d.lineheight() - self.font_ascent
- #
- # Add a word to the list of boxes; first flush if line is full.
- # Space and stretch factors are expressed in fractions
- # of the current font's space width.
- # (Two variations: one without, one with explicit stretch factor.)
- #
- def addword(self, word, spacefactor):
- self.addwordstretch(word, spacefactor, spacefactor)
- #
- def addwordstretch(self, word, spacefactor, stretchfactor):
- width = self.d.textwidth(word)
- if width > self.avail_width:
- self._flush(1)
- space = int(float(self.font_space) * float(spacefactor))
- stretch = int(float(self.font_space) * float(stretchfactor))
- box = (self.font, word, width, space, stretch)
- self.boxes.append(box)
- self.sum_width = self.sum_width + width
- self.sum_space = self.sum_space + space
- self.sum_stretch = self.sum_stretch + stretch
- self.max_ascent = max(self.font_ascent, self.max_ascent)
- self.max_descent = max(self.font_descent, self.max_descent)
- self.avail_width = self.avail_width - width - space
- #
- # Flush current line and start a new one.
- # Flushing twice is harmless (i.e. does not introduce a blank line).
- # (Two versions: the internal one has a parameter for justification.)
- #
- def flush(self):
- self._flush(0)
- #
- def _flush(self, justify):
- if not self.boxes:
- return
- #
- # Compute amount of stretch needed.
- #
- if justify and self.justify or self.center:
- #
- # Compute extra space to fill;
- # this is avail_width plus glue from last box.
- # Also compute available stretch.
- #
- last_box = self.boxes[len(self.boxes)-1]
- font, word, width, space, stretch = last_box
- tot_extra = self.avail_width + space
- tot_stretch = self.sum_stretch - stretch
- else:
- tot_extra = tot_stretch = 0
- #
- # Output the boxes.
- #
- baseline = self.v + self.max_ascent
- h = self.left + self.hang_indent
- if self.center:
- h = h + tot_extra / 2
- tot_extra = tot_stretch = 0
- for font, word, width, space, stretch in self.boxes:
- self.d.setfont(font)
- v = baseline - self.d.baseline()
- self.d.text((h, v), word)
- h = h + width + space
- if tot_extra > 0 and tot_stretch > 0:
- extra = stretch * tot_extra / tot_stretch
- h = h + extra
- tot_extra = tot_extra - extra
- tot_stretch = tot_stretch - stretch
- #
- # Prepare for next line.
- #
- self.v = baseline + self.max_descent
- self.d.setfont(self.font)
- self._reset()
- #
- # Add vertical space; first flush.
- # Vertical space is expressed in fractions of the current
- # font's line height.
- #
- def vspace(self, lines):
- self.vspacepixels(int(lines * self.d.lineheight()))
- #
- # Add vertical space given in pixels.
- #
- def vspacepixels(self, dv):
- self.flush()
- self.v = self.v + dv
- #
- # Set temporary (hanging) indent, for paragraph start.
- # First flush.
- #
- def tempindent(self, space):
- self.flush()
- hang = int(float(self.font_space) * float(space))
- self.hang_indent = hang
- self.avail_width = self.avail_width - hang
- #
- # Add (permanent) left indentation. First flush.
- #
- def addleftindent(self, space):
- self.flush()
- self.left = self.left \
- + int(float(self.font_space) * float(space))
- self._reset()
- #
-
-
- # Test procedure
- #
- def test():
- import stdwin, stdwinq
- from stdwinevents import *
- try:
- import mac
- # Mac font assignments:
- font1 = 'times', '', 12
- font2 = 'times', 'b', 14
- except ImportError:
- # X11R4 font assignments
- font1 = '*times-medium-r-*-120-*'
- font2 = '*times-bold-r-*-140-*'
- words = \
- ['The','quick','brown','fox','jumps','over','the','lazy','dog.']
- words = words * 2
- stage = 0
- stages = [(0,0,'ragged'), (1,0,'justified'), (0,1,'centered')]
- justify, center, title = stages[stage]
- stdwin.setdefwinsize(300,200)
- w = stdwin.open(title)
- winsize = w.getwinsize()
- while 1:
- type, window, detail = stdwinq.getevent()
- if type == WE_CLOSE:
- break
- elif type == WE_SIZE:
- newsize = w.getwinsize()
- if newsize <> winsize:
- w.change((0,0), winsize)
- winsize = newsize
- w.change((0,0), winsize)
- elif type == WE_MOUSE_DOWN:
- stage = (stage + 1) % len(stages)
- justify, center, title = stages[stage]
- w.settitle(title)
- w.change((0, 0), (1000, 1000))
- elif type == WE_DRAW:
- width, height = winsize
- f = formatter(w.begindrawing(), 0, 0, width)
- f.center = center
- f.justify = justify
- if not center:
- f.tempindent(5)
- for font in font1, font2, font1:
- f.setfont(font)
- for word in words:
- space = 1 + (word[-1:] == '.')
- f.addword(word, space)
- if center and space > 1:
- f.flush()
- f.flush()
- height = f.v
- del f
- w.setdocsize(0, height)
-